package com.gj.xxl.job.core.biz.impl;

import com.gj.xxl.job.core.biz.ExecutorBiz;
import com.gj.xxl.job.core.biz.model.ReturnT;
import com.gj.xxl.job.core.biz.model.TriggerParam;
import com.gj.xxl.job.core.block.ExecutorBlockStrategyEnum;
import com.gj.xxl.job.core.executor.GjJobSpringExecutor;
import com.gj.xxl.job.core.thread.JobThread;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.LinkedBlockingQueue;

/**
 * 执行器这端使用的，负责接收到调度中心的通知，然后执行对应的定时任务
 */
public class ExecutorBizImpl implements ExecutorBiz {

    private static Logger logger = LoggerFactory.getLogger(ExecutorBizImpl.class);


    //todo 目前代码存在的问题：
    // 1.目前代码中是通过业务线程池中的线程去执行定时任务，但是假如说存在耗时较长的定时任务，
    // 那么线程池的线程就会被耗尽，那么耗时较短的定时任务不久被拖累了吗？
    // 2.假如有一个定时任务，先被执行了一次，但是由于某些原因被阻塞了，这个时候定时任务又被执行了第二次，
    // 而且第二次任务相较于第一次先被执行完了，那么就会存在并发，数据错乱的问题

    //那么怎么解决呢？
    //既然任务有编号 1 - 5，那么就让线程和任务一一对应就行了
    //thread01 只负责执行 job1, thread02 只负责执行 job2 ...
    //线程内部再封装一个队列，把任务按执行顺序放入队列中
    //这样就可以很好的解决上面的两个问题了
    @Override
    public ReturnT<String> run(TriggerParam triggerParam) {
        JobThread jobThread = GjJobSpringExecutor.loadJobThread(triggerParam.getJobId());

        //上一个任务还没有执行完，这个时候就需要阻塞处理策略了
        if (jobThread != null && jobThread.jobThreadRunning()) {
            ExecutorBlockStrategyEnum blockStrategyEnum = ExecutorBlockStrategyEnum.match(triggerParam.getExecutorBlockStrategy(), null);
            if (ExecutorBlockStrategyEnum.DISCARD_THIS_TIME == blockStrategyEnum) {
                if (jobThread.jobThreadRunning()) {
                    //直接丢此本次任务
                    logger.info("触发任务阻塞处理策略，直接丢弃本次任务了");
                    return new ReturnT<String>(ReturnT.FAIL_CODE, "block strategy effect：" + ExecutorBlockStrategyEnum.DISCARD_THIS_TIME.name());
                }
            } else if (ExecutorBlockStrategyEnum.DISCARD_LAST_TIME == blockStrategyEnum) {
                //丢此上一次没执行完的任务
                jobThread = null;
            } else {
                // 串行，就不做处理，直接把 triggerParam 放入队列中就行了
            }
        }

        if (jobThread == null) {
            jobThread = GjJobSpringExecutor.registerJobThread(triggerParam.getJobId());
        }

        LinkedBlockingQueue<TriggerParam> jobQueue = jobThread.getJobQueue();
        jobQueue.add(triggerParam);

        return ReturnT.SUCCESS;
    }

    @Override
    public ReturnT beat() {
        return ReturnT.SUCCESS;
    }
}
